package org.softee.numbertext;

import java.math.BigDecimal;

public class NumberTextRoman {
    protected static enum RomanNumeral {
        I(1),
        V(5),
        X(10),
        L(50),
        C(100),
        D(500),
        M(1000);

        public static final RomanNumeral[] VALUES = RomanNumeral.values();
        public static final int SIZE = VALUES.length;
        public final int value;

        private RomanNumeral(int value) {
            this.value = value;
        }

        public RomanNumeral next() {
            final int ord = ordinal();
            return (ord < SIZE - 1) ? VALUES[ord + 1] : null;
        }

        public RomanNumeral previous() {
            final int ord = ordinal();
            return (ord > 0) ? VALUES[ord - 1] : null;
        }
    }

    public BigDecimal parse(String romanNumerals) {
       final char[] romanChars = romanNumerals.toCharArray();
       int result = 0;
       int index = 0;
       int length = romanChars.length;
       while (index < length) {
           final RomanNumeral rn = romanNumeral(romanChars[index++]);
           final RomanNumeral next = (index < length) ? romanNumeral(romanChars[index]) : null;
           final boolean nextIsLarger = next != null && next.value > rn.value;
           final int value;
           if (nextIsLarger) {
               value = next.value - rn.value;
               index++; // skip
           } else {
               value = rn.value;
           }
           result += value;
       }
       return new BigDecimal(result);
    }

    private static RomanNumeral romanNumeral(char romanNumeral) {
        return RomanNumeral.valueOf(String.valueOf(romanNumeral));
    }

    public String format(int number) {
        StringBuilder sb = new StringBuilder();
        format(number, sb);
        return sb.toString();
    }

    public void formatNoSubtraction(final int value, StringBuilder sb) {
        int remainder = value;
        while (remainder > 0) {
            RomanNumeral rn = romanLessOrEqual(remainder);
            sb.append(rn);
            remainder = remainder - rn.value;
        }
    }

    public void format(int value, StringBuilder sb) {
        int remainder = value;
        while (remainder > 0) {
            RomanNumeral rn = romanLessOrEqual(remainder);
            if (rn.value < remainder) {
                // now check if we can use higher value with subtractor
                final RomanNumeral higher = rn.next();
                if (higher != null) {
                    final int idealSubtractorValue = higher.value - remainder;
                    RomanNumeral subtractor = romanLargerOrEqual(idealSubtractorValue);
                    if (subtractor.value < higher.value / 2) {
                        sb.append(subtractor).append(higher);
                        remainder = remainder - (higher.value - subtractor.value);
                        continue;
                    }
                }
            }
            sb.append(rn);
            remainder = remainder - rn.value;
        }
    }

    protected static RomanNumeral romanLessOrEqual(int value) {
        RomanNumeral rn = RomanNumeral.VALUES[RomanNumeral.SIZE - 1];
        do {
            if (rn.value <= value) {
                break;
            }
        } while (null != (rn = rn.previous()));
        return rn;
    }

    protected static RomanNumeral romanLargerOrEqual(int value) {
        RomanNumeral rn = RomanNumeral.VALUES[0];
        do {
            if (rn.value >= value) {
                break;
            }
        } while (null != (rn = rn.next()));
        return rn;
    }
}
